package com.packenius.library.xspdf;

import java.util.ArrayList;
import java.util.List;

/**
 * Single text line on a PDF page.
 * @author Christian Packenius.
 */
class XSTextLine {
  /**
   * The column this text line belongs to.
   */
  final XSColumn column;

  /**
   * Upper left corner of this text line within the column (NOT within the page!).
   */
  double xStartInColumn, yStartInColumn;

  /**
   * Text line must not get larger than these values.
   */
  double maxWidth, maxHeight;

  /**
   * Maximal ascender and descender of all used fonts. "minDescender" is zero or negative.
   */
  private double maxAscender, minDescender;

  // Line font baseline := yStart + maxAscender

  /**
   * List of all text parts.
   */
  final List<XSTextPart> textParts = new ArrayList<XSTextPart>();

  /**
   * Current width. Will be higher with every added text part.
   */
  private double currentWidth = 0;

  /**
   * If this value is true and the used text alignment is "justification", than this line should be printed left aligned.
   */
  boolean leftAlignWhenJustification;

  /**
   * Constructor.
   * @param column The column this text line belongs to.
   * @param xStart X coordinate of left top corner of the text field.
   * @param yStart Y coordinate of left top corner of the text field.
   * @param maxWidth Maximal width the line may reach.
   * @param maxHeight Maximal height the line may reach.
   * @param fontUsage Font and font size.
   */
  public XSTextLine(XSColumn column, double xStart, double yStart, double maxWidth, double maxHeight,
      XSFontUsage fontUsage) {
    enlargeAscenderAndDescender(fontUsage);
    this.column = column;
    xStartInColumn = xStart;
    yStartInColumn = yStart;
    this.maxWidth = maxWidth;
    this.maxHeight = maxHeight;
  }

  double getHeight() {
    return maxAscender - minDescender; // minDescender is negative or zero.
  }

  boolean addTextPart(String part, XSFontUsage fontUsage, XSLink link) {
    if (part == null || part.length() == 0 || part.equals("\n")) {
      throw new XSPdfException("Coding error @addTextPart()?");
    }
    enlargeAscenderAndDescender(fontUsage);
    double partWidth = fontUsage.getTextPartWidth(part);
    if (textParts.isEmpty()) {
      setIndentationOnFirstLine(fontUsage);
      if (partWidth > maxWidth) {
        return false;
      }
    } else { // Line not empty.
      if (partWidth + fontUsage.spaceWidth > maxWidth - currentWidth) {
        // Put word into the next line.
        return false;
      }
      switch (fontUsage.formattingMode) {
      case AutoFormat:
        currentWidth += fontUsage.spaceWidth;
        break;
      case NoFormatting:
        break;
      }
    }
    informContentListeners(part, partWidth);
    fontUsage = column.page.xsPDF.getCurrentFontUsage();
    textParts.add(new XSTextPart(part, fontUsage, partWidth, link));
    return true;
  }

  private void informContentListeners(String part, double partWidth) {
    currentWidth += partWidth;
    XSPage page = column.page;
    XSPDF xsPDF = page.xsPDF;
    int pageID = xsPDF.pages.size();
    int textLineID = page.columns.size();
    for (XSContentListener listener : xsPDF.contentListeners) {
      listener.newTextPart(xsPDF, pageID, column.name, textLineID, part);
    }
  }

  private void setIndentationOnFirstLine(XSFontUsage fontUsage) {
    double additionalIndentation = calculateCurrentLineIndentation(fontUsage);
    xStartInColumn += additionalIndentation;
    maxWidth -= additionalIndentation;
  }

  private double calculateCurrentLineIndentation(XSFontUsage fontUsage) {
    if (fontUsage.alignment == XSAlignment.Justification || fontUsage.alignment == XSAlignment.LeftAligned) {
      XSPDF xsPDF = column.page.xsPDF;
      if (xsPDF.lastTextPart.equals("\n")) {
        return fontUsage.spaceWidth * xsPDF.currentTextParagraphIndentationSpaces;
      }
    }
    return 0;
  }

  private void enlargeAscenderAndDescender(XSFontUsage fontUsage) {
    if (textParts.isEmpty()) {
      maxAscender = 0;
      minDescender = 0;
    }
    XST1StdFont fontClass = fontUsage.fontType.fontClass;
    double fontSize = fontUsage.fontSize;
    maxAscender = Math.max(maxAscender, fontClass.getAscender() * fontSize);
    minDescender = Math.min(minDescender, fontClass.getDescender() * fontSize);
    if (minDescender > 0) {
      throw new XSPdfException("<minDescender> can't be larger than zero, can it?");
    }
  }

  double getBaselineYInColumn() {
    return yStartInColumn - maxAscender;
  }

  double getRemainingSpace() {
    return maxWidth - currentWidth;
  }

  boolean hasContent() {
    return !textParts.isEmpty();
  }
}
